/*
 * Copyright (c) 2008 Benoit Bouchez, Nicolas Giard
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package com.noocodecommit.wicket.stuff.jquery.core;

/**
 * JQSelector builds JQuery selectors and provides functions to filter
 * 
 * @author Benoit Bouchez
 */
public class JQSelector
{
	private StringBuilder selectorBuilder = new StringBuilder();

	/**
	 * Constructor
	 * 
	 * @param selector
	 *            jquery selector
	 */
	public JQSelector(String selector)
	{
		this.selectorBuilder.append(selector);
	}

	/**
	 * Matches all child elements specified by "child" of elements specified by "parent".
	 * 
	 * @param child
	 *            child selector
	 * @return
	 */
	public JQSelector child(JQSelector child)
	{
		selectorBuilder.append(" > " + child);
		return this;
	}

	/**
	 * Matches all child elements specified by "child" of elements specified by "parent".
	 * 
	 * @param child
	 *            child selector
	 * @return
	 */
	public JQSelector child(String child)
	{
		selectorBuilder.append(" > " + child);
		return this;
	}

	/**
	 * Matches all descendant elements specified by "descendant" of elements specified by "ancestor".
	 * 
	 * @param descendant
	 *            descendant selector
	 * @return
	 */
	public JQSelector descendant(JQSelector descendant)
	{
		selectorBuilder.append(" " + descendant);
		return this;
	}

	/**
	 * Matches all descendant elements specified by "descendant" of elements specified by "ancestor".
	 * 
	 * @param descendant
	 *            descendant selector
	 * @return
	 */
	public JQSelector descendant(String descendant)
	{
		selectorBuilder.append(" " + descendant);
		return this;
	}

	/**
	 * Matches all next elements specified by "next" that are next to elements specified by "prev".
	 * 
	 * @param next
	 *            next selector
	 * @return
	 */
	public JQSelector next(JQSelector next)
	{
		selectorBuilder.append(" + " + next);
		return this;
	}

	/**
	 * Matches all next elements specified by "next" that are next to elements specified by "prev".
	 * 
	 * @param next
	 *            next selector
	 * @return
	 */
	public JQSelector next(String next)
	{
		selectorBuilder.append(" + " + next);
		return this;
	}

	/**
	 * Matches all sibling elements after the "prev" element that match the filtering "siblings" selector.
	 * 
	 * @param siblings
	 *            siblings selector
	 * @return
	 */
	public JQSelector siblings(JQSelector siblings)
	{
		selectorBuilder.append(" ~ " + siblings);
		return this;
	}

	/**
	 * Matches all sibling elements after the "prev" element that match the filtering "siblings" selector.
	 * 
	 * @param siblings
	 *            siblings selector
	 * @return
	 */
	public JQSelector siblings(String siblings)
	{
		selectorBuilder.append(" ~ " + siblings);
		return this;
	}

	/**
	 * Matches all elements that are currently being animated.
	 * 
	 * @return
	 */
	public JQSelector animated()
	{
		selectorBuilder.append(":animated");
		return this;
	}

	/**
	 * Matches a single element by its index.
	 * 
	 * @param index
	 *            element index
	 * @return
	 */
	public JQSelector eq(Number index)
	{
		selectorBuilder.append(":eq(" + index + ")");
		return this;
	}

	/**
	 * Matches even elements, zero-indexed.
	 * 
	 * @return
	 */
	public JQSelector even()
	{
		selectorBuilder.append(":even");
		return this;
	}

	/**
	 * Matches the first selected element.
	 * 
	 * @return
	 */
	public JQSelector first()
	{
		selectorBuilder.append(":first");
		return this;
	}

	/**
	 * Matches all elements with an index above the given one.
	 * 
	 * @param index
	 *            index
	 * @return
	 */
	public JQSelector gt(Float index)
	{
		selectorBuilder.append(":gt(" + index + ")");
		return this;
	}

	/**
	 * Matches all elements that are headers, like h1, h2, h3 and so on.
	 * 
	 * @return
	 */
	public JQSelector header()
	{
		selectorBuilder.append(":header");
		return this;
	}

	/**
	 * Matches the last selected element.
	 * 
	 * @return
	 */
	public JQSelector last()
	{
		selectorBuilder.append(":last");
		return this;
	}

	/**
	 * Matches all elements with an index below the given one.
	 * 
	 * @param index
	 *            index
	 * @return
	 */
	public JQSelector lt(Float index)
	{
		selectorBuilder.append(":lt(" + index + ")");
		return this;
	}

	/**
	 * Filters out all elements matching the given selector.
	 * 
	 * @param selector
	 *            jquery selector
	 * @return
	 */
	public JQSelector not(JQSelector selector)
	{
		selectorBuilder.append(":not(" + selector + ")");
		return this;
	}

	/**
	 * Matches odd elements, zero-indexed.
	 * 
	 * @return
	 */
	public JQSelector odd()
	{
		selectorBuilder.append(":odd");
		return this;
	}

	/**
	 * Matches elements which contain the given text.
	 * 
	 * @param text
	 *            element text
	 * @return
	 */
	public JQSelector contains(String text)
	{
		selectorBuilder.append(":contains('" + text + "')");
		return this;
	}

	/**
	 * Matches all elements that have no children (including text nodes).
	 * 
	 * @return
	 */
	public JQSelector empty()
	{
		selectorBuilder.append(":empty");
		return this;
	}

	/**
	 * Matches elements which contain at least one element that matches the specified selector.
	 * 
	 * @param selector
	 *            jquery selector
	 * @return
	 */
	public JQSelector has(JQSelector selector)
	{
		selectorBuilder.append(":has(" + selector + ")");
		return this;
	}

	/**
	 * Matches all elements that are parents - they have child elements, including text.
	 * 
	 * @return
	 */
	public JQSelector parent()
	{
		selectorBuilder.append(":parent");
		return this;
	}

	/**
	 * Matches all elements that are hidden, or input elements of type "hidden".
	 * 
	 * @return
	 */
	public JQSelector hidden()
	{
		selectorBuilder.append(":hidden");
		return this;
	}

	/**
	 * Matches all elements that are visible.
	 * 
	 * @return
	 */
	public JQSelector visible()
	{
		selectorBuilder.append(":visible");
		return this;
	}

	/**
	 * Matches elements that have the specified attribute and it contains a certain value.
	 * 
	 * @param attribute
	 *            attribute name
	 * @param value
	 *            attribute value
	 * @return
	 */
	public JQSelector attributeContains(String attribute, String value)
	{
		selectorBuilder.append("[" + attribute + "*='" + value + "']");
		return this;
	}

	/**
	 * Matches elements that have the specified attribute and it ends with a certain value.
	 * 
	 * @param attribute
	 *            attribute name
	 * @param value
	 *            attribute value
	 * @return
	 */
	public JQSelector attributeEndsWith(String attribute, String value)
	{
		selectorBuilder.append("[" + attribute + "$='" + value + "']");
		return this;
	}

	/**
	 * Matches elements that have the specified attribute with a certain value.
	 * 
	 * @param attribute
	 *            attribute name
	 * @param value
	 *            attribute value
	 * @return
	 */
	public JQSelector attributeEquals(String attribute, String value)
	{
		selectorBuilder.append("[" + attribute + "='" + value + "']");
		return this;
	}

	/**
	 * Matches elements that have the specified attribute.
	 * 
	 * @param attribute
	 *            attribute name
	 * @return
	 */
	public JQSelector attributeHas(String attribute)
	{
		selectorBuilder.append("[" + attribute + "]");
		return this;
	}

	/**
	 * Matches elements that have the specified attribute and it contains a certain value.
	 * 
	 * @param selectors
	 *            attribute selector array
	 * @return
	 */
	public JQSelector attributeMultiple(JQSelector[] selectors)
	{
		for (int i = 0; i < selectors.length; i++)
		{
			selectorBuilder.append("[" + selectors[i] + "]");
		}
		return this;
	}

	/**
	 * Matches elements that don't have the specified attribute with a certain value.
	 * 
	 * @param attribute
	 *            attribute name
	 * @param value
	 *            attribute value
	 * @return
	 */
	public JQSelector attributeNotEqual(String attribute, String value)
	{
		selectorBuilder.append("[" + attribute + "!='" + value + "']");
		return this;
	}

	/**
	 * Matches elements that have the specified attribute and it starts with a certain value.
	 * 
	 * @param attribute
	 *            attribute name
	 * @param value
	 *            attribute value
	 * @return
	 */
	public JQSelector attributeStartsWith(String attribute, String value)
	{
		selectorBuilder.append("[" + attribute + "^='" + value + "']");
		return this;
	}

	/**
	 * Matches the first child of its parent.
	 * 
	 * @return
	 */
	public JQSelector firstChild()
	{
		selectorBuilder.append(":first-child");
		return this;
	}

	/**
	 * Matches the last child of its parent.
	 * 
	 * @return
	 */
	public JQSelector lastChild()
	{
		selectorBuilder.append(":last-child");
		return this;
	}

	/**
	 * Matches the nth-child of its parent or all its even or odd children.
	 * 
	 * @param index
	 *            child index
	 * @return
	 */
	public JQSelector nthChild(Number index)
	{
		selectorBuilder.append(":nth-child(" + index + ")");
		return this;
	}

	/**
	 * Matches the nth-child of its parent or all its even or odd children.
	 * 
	 * @param index
	 *            child index
	 * @return
	 */
	public JQSelector nthChild(String index)
	{
		selectorBuilder.append(":nth-child('" + index + "')");
		return this;
	}

	/**
	 * Matches the only child of its parent.<br>
	 * If the parent has other child elements, nothing is matched.
	 * 
	 * @return
	 */
	public JQSelector onlyChild()
	{
		selectorBuilder.append(":only-child");
		return this;
	}

	/**
	 * Matches all elements that are checked.
	 * 
	 * @return
	 */
	public JQSelector checked()
	{
		selectorBuilder.append(":checked");
		return this;
	}

	/**
	 * Matches all elements that are disabled.
	 * 
	 * @return
	 */
	public JQSelector disabled()
	{
		selectorBuilder.append(":disabled");
		return this;
	}

	/**
	 * Matches all elements that are enabled.
	 * 
	 * @return
	 */
	public JQSelector enabled()
	{
		selectorBuilder.append(":enabled");
		return this;
	}

	/**
	 * Matches all elements that are selected.
	 * 
	 * @return
	 */
	public JQSelector selected()
	{
		selectorBuilder.append(":selected");
		return this;
	}

	/**
	 * Builds the selector
	 * 
	 * @return
	 */
	public String build()
	{
		return this.toString();
	}

	@Override
	public String toString()
	{
		return selectorBuilder.toString();
	}
}
